#include "Camera.h"

Camera::Camera()
{
    visible = false;
    center[0] = center[1] = center[2] = 0.0f;
    up[0] = 0.0f; up[1] = 1.0f; up[2] = 0.0f;
}

void Camera::setup()
{
    // Specify Eye position vector, Center position vector, Up vector
    gluLookAt(x, y, z, center[0], center[1], center[2], up[0], up[1], up[2]);
}

void Camera::setEye(GLfloat ex, GLfloat ey, GLfloat ez)
{
    x = ex;
    y = ey;
    z = ez;
}

void Camera::setCenter(GLfloat cx, GLfloat cy, GLfloat cz)
{
    center[0] = cx;
    center[1] = cy;
    center[2] = cz;
}

void Camera::setUp(GLfloat ux, GLfloat uy, GLfloat uz)
{
	up[0] = ux;
	up[1] = uy;
	up[2] = uz;
}

// First person free view camera
FreeViewCamera::FreeViewCamera()
{
    lineSight[0] = lineSight[1] = lineSight[2] = 0.0f;

    yAngle = xAngle = 0.0f;

    move = 0;
    strafe = 0;

    warpedEvent = true; // Ignore the very first event, otherwise the camera
                        // may end up in a funky place even when the user has
                        // not touched their controls.
}

void FreeViewCamera::update(Uint32* deltaTime)
{
    lineSight[0] = sin(yAngle)*cos(xAngle);
    lineSight[1] = sin(xAngle);
    lineSight[2] = cos(yAngle)*-cos(xAngle);

    float deltaTimeSec = (*deltaTime)/1000.0f;

	if (move != 0)
	{
		x += lineSight[0]*(float)move*deltaTimeSec;
		y += lineSight[1]*(float)move*deltaTimeSec;
		z += lineSight[2]*(float)move*deltaTimeSec;
	}
	if (strafe != 0)
	{
        x += cos(yAngle)*(float)strafe*deltaTimeSec;
        z += sin(yAngle)*(float)strafe*deltaTimeSec;
	}

    center[0] = x + lineSight[0];
    center[1] = y + lineSight[1];
    center[2] = z + lineSight[2];
}

void FreeViewCamera::handleEvents(SDL_Event* event)
{
    if (event->type == SDL_KEYDOWN)
    {
        if (event->key.keysym.sym == SDLK_w)
        {
            // Move forward
            move = 6;
        }
        else if (event->key.keysym.sym == SDLK_s)
        {
            // Move backward
            move = -6;
        }
        else if (event->key.keysym.sym == SDLK_a)
        {
            // Strafe left
            strafe = -4;
        }
        else if (event->key.keysym.sym == SDLK_d)
        {
            // Strafe right
            strafe = 4;
        }
    }
    else if (event->type == SDL_KEYUP)
    {
        if (event->key.keysym.sym == SDLK_w
            || event->key.keysym.sym == SDLK_s)
        {
            move = 0;
        }
        else if (event->key.keysym.sym == SDLK_a
            || event->key.keysym.sym == SDLK_d)
        {
            strafe = 0;
        }
    }
    else if (event->type == SDL_MOUSEMOTION)
    {
        if (!warpedEvent)
        {
            xAngle -= (float)(event->motion.yrel/128.0f);
            yAngle += (float)(event->motion.xrel/128.0f);

            // Don't let the camera look down too low or it'll flip
            // (This implementation is not perfect but good enough for now)
            if (xAngle < -PID2)
                xAngle = -PID2;
            else if (xAngle > PID2)
                xAngle = PID2;

            // For debugging:
            //std::cout << xAngle << " " << yAngle << std::endl;

            SDL_WarpMouse(Engine::Instance().getScreenWidth()/2,
                          Engine::Instance().getScreenHeight()/2);
            warpedEvent = true; // We want to ignore the following event that SDL_WrapMouse sends
            SDL_ShowCursor(SDL_DISABLE); // Make sure mouse cursor is hidden
        }
        else
        {
            warpedEvent = false;
        }
    }
}

// Scene Rotation Camera (spherical), inherits from Camera class
SceneRotateCamera::SceneRotateCamera()
{
    distance = 4.0f;
    theta = 0.0f;
    phi = 45.0f;

    warpedEvent = true;
}

void SceneRotateCamera::update(Uint32* deltaTime)
{
    // (Note that we are having to convert to radians)
    static float rad = PI/180.0f;

    y = distance*cos(rad*phi);
    rCylindrical = distance*sin(rad*phi);
    x = rCylindrical*cos(rad*theta);
    z = rCylindrical*sin(rad*theta);
}

void SceneRotateCamera::handleEvents(SDL_Event* event)
{
    if (event->type == SDL_MOUSEMOTION)
    {
        if (!warpedEvent)
        {
            float dy = (float)(event->motion.yrel);
            float dx = (float)(event->motion.xrel);

            phi -= dy*0.2f;
            theta -= dx*0.2f;

            // Limit how far the camera can move
            if (phi > 89.0f)
                phi = 89.0f;
            if (phi < 1.0f)
                phi = 1.0f;

            SDL_WarpMouse(Engine::Instance().getScreenWidth()/2,
                          Engine::Instance().getScreenHeight()/2);
            warpedEvent = true; // We want to ignore the following event that SDL_WrapMouse sends
            SDL_ShowCursor(SDL_DISABLE); // Make sure mouse cursor is hidden
        }
        else
        {
            warpedEvent = false;
        }
    }
}

void SceneRotateCamera::setPhi(GLfloat newPhi)
{
    phi = newPhi;
}

void SceneRotateCamera::setTheta(GLfloat newTheta)
{
    theta = newTheta;
}

void SceneRotateCamera::setDistance(GLfloat newDistance)
{
    distance = newDistance;
}

void SceneRotateCamera::addDistance(GLfloat dist)
{
    distance += dist;
}
